#!/bin/sh
# Copyright (C) BlueWave Projects and Services 2015-2024
#
#	This software is released under the GNU General Public License version 3 or any later version.
#	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,
#	either version 3 of the License, or (at your option) any later version.
#
#	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#	See the GNU General Public License for more details.
#
#	To obtain a copy of the GNU General Public License, see <https://www.gnu.org/licenses/>.
#
# mesh11sd daemon
#
version="3.1.1beta"
fixup1=0

get_mesh_iflist () {
	iflist=""
	all_ifcs=$(iw dev | awk -F "Interface " '$2>0{printf "%s " $2}')

	for iface in $all_ifcs; do
		iftype=$(iw dev $iface info 2> /dev/null | grep "type" | awk '{printf "%s", $2}')

		if [ ! -z "$iftype" ] && [ "$iftype" = "mesh" ]; then
			iflist="$iflist $iface"
		fi
	done
}

check_mesh_phantom () {
	nodelist=$(iw dev $iface mpath dump | awk '{printf "%s,%s ", $1, $2}')

	for node in $nodelist; do

		debugtype="debug"
		syslogmessage="node [$node]"
		write_to_syslog

		phantom=$(echo "$node" | awk -F"," '$2 == "00:00:00:00:00:00" {printf $1 }')

		if [ ! -z "$phantom" ]; then
			debugtype="info"
			syslogmessage="Phantom meshnode detected [$phantom] - deleting"
			write_to_syslog
			iw dev $iface mpath del $phantom
		fi
	done
}

get_params () {
	params=$(iw dev $iface mesh_param dump 2> /dev/null)

	if [ "$?" -ne 0 ]; then
		buffer="/tmp/"$(date | sha256sum | awk '{printf $1}')
		param_list=$(iw dev $iface get mesh_param 2> /dev/null | grep " - " | awk -F" - " '{printf "%s ", $2}')
		params=""


		for param in $param_list; do
			paramval=$(iw dev $iface get mesh_param $param 2> /dev/null)
			if [ "$?" -eq 0 ]; then
				paramline="$param = $paramval"
				echo "$paramline" >> $buffer
			fi
		done



		if [ -f "$buffer" ]; then
			params=$(cat $buffer)
			rm "$buffer"
		fi
	fi
}

check_mesh_params () {

	for param in $params; do

		conf=$(uci get $uciname.$param 2> /dev/null)

		if [ -z "$conf" ]; then
			continue
		fi

		param_value=$(iw dev $iface get mesh_param $param 2> /dev/null | awk '{printf "%s", $1}')

		if [ -z "$param_value" ]; then
			debugtype="info"
			syslogmessage="Failed to get current value of $param, mesh interface not established."
			write_to_syslog
			continue
		fi

		if [ "$param_value" != "$conf" ]; then

			if [ "$param" = "mesh_rssi_threshold" ]; then
				if [ ! -f "/tmp/mesh_rssi_threshold" ]; then
					debugtype="info"
					syslogmessage="Old value:$param=$param_value, Setting new value:$param=$conf"
					write_to_syslog
					iwstatus=$(iw dev $iface set mesh_param $param "$conf" 2>&1)
					mesh_rssi_threshold=$conf
					echo "meshinterface=\"$iface\"" > /tmp/meshinterface
					all_nodes_rssi_update
				fi
			elif [ "$param" != "mesh_rssi_threshold" ]; then
				debugtype="info"
				syslogmessage="Old value:$param=$param_value, Setting new value:$param=$conf"
				write_to_syslog
				iwstatus=$(iw dev $iface set mesh_param $param "$conf" 2>&1)
			fi


			if [ ! -z "$iwstatus" ]; then
				debugtype="err"
				syslogmessage="$param: $iwstatus"
				write_to_syslog
			fi
		fi
	done
}

restart_mesh () {
	set_txpower
	mesh_gate_state
	wifi
	wait_for_mesh

	if [ -f "/tmp/devicemac" ]; then
		. /tmp/devicemac
		ip link set "$device" arp on
		ip link set "$device" multicast on
	fi

}

get_current_setup () {
	debuglevel=$(uci get mesh11sd.setup.debuglevel 2> /dev/null)

	if [ -z "$debuglevel" ]; then
		debuglevel=1
	fi

	syslogmessage="Reading configuration"
	debugtype="notice"
	write_to_syslog

	enabled=$(uci get mesh11sd.setup.enabled 2> /dev/null)

	if [ -z "$enabled" ]; then
		enabled=1
	fi

	syslogmessage="option enabled [ $enabled ]"
	debugtype="info"
	write_to_syslog

	#####
	syslogmessage="option debuglevel [ $debuglevel ]"
	debugtype="info"
	write_to_syslog

	#####
	checkinterval=$(uci get mesh11sd.setup.checkinterval 2> /dev/null)

	if [ -z "$checkinterval" ] || [ "$checkinterval" -lt 10 ]; then
		checkinterval=10
	fi

	syslogmessage="option checkinterval [ $checkinterval ]"
	debugtype="info"
	write_to_syslog

	#####
	portal_detect=$(uci get mesh11sd.setup.portal_detect 2> /dev/null)

	if [ -z "$portal_detect" ]; then
		portal_detect=1
	fi

	syslogmessage="option portal_detect [ $portal_detect ]"
	debugtype="info"
	write_to_syslog

	#####
	portal_channel=$(uci get mesh11sd.setup.portal_channel 2> /dev/null)

	if [ -z "$portal_channel" ]; then
		portal_channel="default"
	fi

	syslogmessage="option portal_channel [ $portal_channel ]"
	debugtype="info"
	write_to_syslog

	#####
	get_mesh_path_cost

	#####
	interface_timeout=$(uci get mesh11sd.setup.interface_timeout 2> /dev/null)

	if [ -z "$interface_timeout" ] || [ "$interface_timeout" -lt 10 ]; then
		interface_timeout=10
	fi

	syslogmessage="option interface_timeout [ $interface_timeout ]"
	debugtype="info"
	write_to_syslog

	#####
	auto_config=$(uci get mesh11sd.setup.auto_config 2> /dev/null)

	if [ -z "$auto_config" ] || [ "$auto_config" -gt 0 ]; then
		auto_config=1
	fi

	syslogmessage="option auto_config [ $auto_config ]"
	debugtype="info"
	write_to_syslog

	#####
	auto_mesh_id=$(uci get mesh11sd.setup.auto_mesh_id 2> /dev/null | awk '{printf "%s", $1}')

	if [ -z "$auto_mesh_id" ]; then
		auto_mesh_id="--__"
	fi

	auto_mesh_id=$(printf "$auto_mesh_id" | sha256sum | awk '{printf $1}' | tail -c55 | head -c30)

	syslogmessage="auto_mesh_id hash [ $auto_mesh_id ]"
	debugtype="info"
	write_to_syslog

	#####
	auto_mesh_band=$(uci get mesh11sd.setup.auto_mesh_band 2> /dev/null | awk '{printf "%s", $1}')

	if [ -z "$auto_mesh_band" ]; then
		auto_mesh_band="2g40"
	fi

	bandlist="2g 5g 6g 60g"
	bandcheck=0

	for band in $bandlist; do

		if [ "$band" = "$auto_mesh_band" ]; then
			bandcheck=1
			break
		fi

	done

	if [ "$bandcheck" = 0 ]; then
		auto_mesh_band="2g40"
	fi

	syslogmessage="option auto_mesh_band [ $auto_mesh_band ]"
	debugtype="info"
	write_to_syslog

	#####
	auto_mesh_key=$(uci get mesh11sd.setup.auto_mesh_key 2> /dev/null | awk '{printf "%s", $1}')

	if [ -z "$auto_mesh_key" ]; then
		auto_mesh_key=$(echo "$auto_mesh_id""91b39a9b41e918e9bce1c8d5d7d3e071d6f1f8855a7c0214f687550177c5d5b8" | sha256sum | awk '{printf "%s", $1}')
	else
		auto_mesh_key=$(echo "$auto_mesh_id""$auto_mesh_key" | sha256sum | awk '{printf "%s", $1}')
	fi

	syslogmessage="option auto_mesh_key [ $auto_mesh_key ]"
	debugtype="info"
	write_to_syslog

	#####
	auto_mesh_network=$(uci get mesh11sd.setup.auto_mesh_network 2> /dev/null)

	if [ -z "$auto_mesh_network" ]; then
		auto_mesh_network="lan"
	fi

	# Block wan and wan6 as valid network zones for mesh

	if [ "$auto_mesh_network" = "wan" ] || [ "$auto_mesh_network" = "wan6" ]; then
		auto_mesh_network="lan"
	fi

	zone_status=$(uci show firewall | grep -w "name='$auto_mesh_network'" &>/dev/null ; echo $?)

	if [ "$zone_status" -eq 1 ]; then
		syslogmessage="network zone [ $auto_mesh_network ] does not exist"
		debugtype="warn"
		write_to_syslog
		auto_mesh_network="lan"
	else
		syslogmessage="option auto_mesh_network [ $auto_mesh_network ]"
		debugtype="info"
		write_to_syslog
	fi
	syslogmessage="option auto_mesh_network [ $auto_mesh_network ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_basename=$(uci get mesh11sd.setup.mesh_basename 2> /dev/null)

	if [ -z "$mesh_basename" ]; then
		mesh_basename="m-11s-"
	else
		#remove non alphanumeric
		mesh_basename=$(echo "$mesh_basename" | sed 's/[^a-zA-Z0-9]//g')
		#get first 4 chars
		mesh_basename="m-${mesh_basename:0:4}-"
		#convert to lower case
		mesh_basename=$(printf $mesh_basename | tr '[A-Z]' '[a-z]')
	fi

	syslogmessage="option mesh_basename [ $mesh_basename ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_gate_enable=$(uci get mesh11sd.setup.mesh_gate_enable 2> /dev/null)

	if [ -z "$mesh_gate_enable" ]; then
		mesh_gate_enable="1"
	fi

	syslogmessage="option mesh_gate_enable [ $mesh_gate_enable ]"
	debugtype="info"
	write_to_syslog

	#####
	txpower=$(uci get mesh11sd.setup.txpower 2> /dev/null)

	if [ ! -z "$txpower" ]; then
		syslogmessage="option txpower [ $txpower ]"
		debugtype="info"
		write_to_syslog
	fi

	#####
	ssid_suffix_enable=$(uci get mesh11sd.setup.ssid_suffix_enable 2> /dev/null)

	if [ -z "$ssid_suffix_enable" ]; then
		ssid_suffix_enable="1"
	fi

	syslogmessage="option ssid_suffix_enable [ $ssid_suffix_enable ]"
	debugtype="info"
	write_to_syslog
}

check_config_params() {
	################
	# Set default parameters if not in config

	#####
	mesh_fwding=$(uci get mesh11sd.mesh_params.mesh_fwding 2> /dev/null)

	if [ -z "$mesh_fwding" ]; then
		mesh_fwding="1"
		echo "set mesh11sd.mesh_params.mesh_fwding='$mesh_fwding'" | uci batch
	fi

	syslogmessage="option mesh_fwding [ $mesh_fwding ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_rssi_threshold=$(uci get mesh11sd.mesh_params.mesh_rssi_threshold 2> /dev/null)

	if [ -z "$mesh_rssi_threshold" ] || [ "$mesh_rssi_threshold" -gt 0 ]; then
		mesh_rssi_threshold="-65"
		echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
	fi

	syslogmessage="option mesh_rssi_threshold [ $mesh_rssi_threshold ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_gate_announcements=$(uci get mesh11sd.mesh_params.mesh_gate_announcements 2> /dev/null)

	if [ -z "$mesh_gate_announcements" ]; then
		mesh_gate_announcements="1"
		echo "set mesh11sd.mesh_params.mesh_gate_announcements='$mesh_gate_announcements'" | uci batch
	fi

	syslogmessage="option mesh_gate_announcements [ $mesh_gate_announcements ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_hwmp_rootmode=$(uci get mesh11sd.mesh_params.mesh_hwmp_rootmode 2> /dev/null)

	if [ -z "$mesh_hwmp_rootmode" ]; then
		mesh_hwmp_rootmode="4"
		echo "set mesh11sd.mesh_params.mesh_hwmp_rootmode='$mesh_hwmp_rootmode'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_rootmode [ $mesh_hwmp_rootmode ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_hwmp_rann_interval=$(uci get mesh11sd.mesh_params.mesh_hwmp_rann_interval 2> /dev/null)

	if [ -z "$mesh_hwmp_rann_interval" ]; then
		mesh_hwmp_rann_interval="5000"
		echo "set mesh11sd.mesh_params.mesh_hwmp_rann_interval='$mesh_hwmp_rann_interval'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_rann_interval [ $mesh_hwmp_rann_interval ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_hwmp_root_interval=$(uci get mesh11sd.mesh_params.mesh_hwmp_root_interval 2> /dev/null)

	if [ -z "$mesh_hwmp_root_interval" ]; then
		mesh_hwmp_root_interval="5000"
		echo "set mesh11sd.mesh_params.mesh_hwmp_root_interval='$mesh_hwmp_root_interval'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_root_interval [ $mesh_hwmp_root_interval ]"
	debugtype="info"
	write_to_syslog

	#####
	mesh_hwmp_active_path_timeout=$(uci get mesh11sd.mesh_params.mesh_hwmp_active_path_timeout 2> /dev/null)

	if [ -z "$mesh_hwmp_active_path_timeout" ]; then
		mesh_hwmp_active_path_timeout="5000"
		echo "set mesh11sd.mesh_params.mesh_hwmp_active_path_timeout='$mesh_hwmp_active_path_timeout'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_active_path_timeout [ $mesh_hwmp_active_path_timeout ]"
	debugtype="info"
	write_to_syslog

	#####

	mesh_hwmp_active_path_to_root_timeout=$(uci get mesh11sd.mesh_params.mesh_hwmp_active_path_to_root_timeout 2> /dev/null)

	if [ -z "$mesh_hwmp_active_path_to_root_timeout" ]; then
		mesh_hwmp_active_path_to_root_timeout="6000"
		echo "set mesh11sd.mesh_params.mesh_hwmp_active_path_to_root_timeout='$mesh_hwmp_active_path_to_root_timeout'" | uci batch
	fi

	syslogmessage="option mesh_hwmp_active_path_to_root_timeout [ $mesh_hwmp_active_path_to_root_timeout ]"
	debugtype="info"
	write_to_syslog

	#####

	mesh_max_peer_links=$(uci get mesh11sd.mesh_params.mesh_max_peer_links 2> /dev/null)

	if [ -z "$mesh_max_peer_links" ]; then
		mesh_max_peer_links="16"
		echo "set mesh11sd.mesh_params.mesh_max_peer_links='$mesh_max_peer_links'" | uci batch
	fi

	syslogmessage="option mesh_max_peer_links [ $mesh_max_peer_links ]"
	debugtype="info"
	write_to_syslog

}

get_mesh_path_cost () {
	mesh_path_cost=$(uci get mesh11sd.setup.mesh_path_cost 2> /dev/null)

	if [ -z "$mesh_path_cost" ]; then
		mesh_path_cost="10"
	fi

	syslogmessage="option mesh_path_cost [ $mesh_path_cost ]"
	debugtype="info"
	write_to_syslog
}

wait_for_interface () {
	local ifname="$1"
	local timeout=$interface_timeout

	for i in $(seq $timeout); do
		if [ $(ip link show $ifname 2> /dev/null | grep -c -w "state UP") -eq 1 ]; then
			ifstatus="up"
			break
		fi
		sleep 1
		if [ $i == $timeout ] ; then
			syslogmessage="$ifname is not up - giving up for now."
			debugtype="notice"
			write_to_syslog
			ifstatus="down"
		fi
	done
}

# Write debug message to syslog
# $syslogmessage contains the string to log
# $debugtype contains the debug level string: debug, info, warn, notice, err, emerg.
write_to_syslog() {

	if [ "$mute" = 0 ]; then
		debuglevel=$(uci get mesh11sd.setup.debuglevel 2> /dev/null)

		if [ -z "$debuglevel" ]; then
			debuglevel=1
		fi

		if [ ! -z "$syslogmessage" ]; then

			case $debugtype in
				"emerg") debugnum=0;;
				"err") debugnum=0;;
				"notice") debugnum=1;;
				"warn") debugnum=1;;
				"info") debugnum=2;;
				"debug") debugnum=3;;
				*) debugnum=1; debugtype="notice";;
			esac

			if [ "$debuglevel" -ge "$debugnum" ]; then
				echo "$syslogmessage" | logger -p "daemon.$debugtype" -s -t "mesh11sd[$mesh11sdpid]"
			fi
		fi
	else
		mute=0
	fi
}

mesh_gate_state() {
	aplist=$(uci show wireless | grep "mode='ap'" | awk -F "." '{printf "%s ", $2}')

	for gate in $aplist; do
		config_state=$(echo "get wireless.$gate.disabled" | uci batch 2> /dev/null)

		if [ -z "$config_state" ] && [ "$mesh_gate_enable" -eq 1 ]; then
			ucibatch="set wireless.$gate.disabled='0'"
			echo "$ucibatch" | uci batch
		elif [ -z "$config_state" ] && [ "$mesh_gate_enable" -eq 0 ]; then
			ucibatch="set wireless.$gate.disabled='1'"
			echo "$ucibatch" | uci batch
		fi
	done
}

set_txpower() {

	if [ ! -z "$txpower" ]; then
		radiolist=$(uci show wireless | grep "wifi-device" | awk -F "=" '{printf "%s ", $1}')

		for radio in $radiolist; do
			config_state=$(echo "get $radio.txpower" | uci batch 2> /dev/null)

			if [ -z "$config_state" ]; then
				ucibatch="set $radio.txpower='$txpower'"
				echo "$ucibatch" | uci batch
			fi
		done
	fi
}


check_gate() {
	is_gate=$(iw dev | grep -w "type AP")

	if [ -z "$is_gate" ]; then
		uci set mesh11sd.mesh_params.mesh_connected_to_gate='0'
	else
		uci set mesh11sd.mesh_params.mesh_connected_to_gate='1'
	fi
}

check_channel() {
	# Can we see the portal?
	link_to_portal=0

	# We need to scan for working channel if we cannot see a portal
	# Check for portal every checkinterval_multiplier checkintervals
	checkinterval_multiplier=2

	timenow=$(date +%s)

	debugtype="debug"
	syslogmessage="checking for working channel...."
	write_to_syslog

	if [ -z $cctimestamp ]; then
		cctimestamp=$(date +%s)
	fi

	elapsed_time=$(($timenow - $cctimestamp))
	mininterval=$(($checkinterval * $checkinterval_multiplier))

	if [ "$mininterval" -le "$elapsed_time" ]; then
		ifaces=$(uci export wireless | grep "config wifi-iface" | awk -F"'" '$2 != "" {printf "%s ",$2}')

		for iface in $ifaces; do
			ucibatch="get wireless.$iface.mode"
			mode=$(echo "$ucibatch" | uci batch)

			if [ "$mode" = "mesh" ]; then
				ucibatch="get wireless.$iface.network"
				network=$(echo "$ucibatch" | uci batch)
				ucibatch="get network.$network.device"
				device=$(echo "$ucibatch" | uci batch)

				if [ -f "/tmp/dhcp6probe" ]; then
					mv "/tmp/dhcp6probe" "/tmp/dhcp6probe.prev"
				fi

				odhcp6c -ev "$device" 2>/tmp/dhcp6probe &

				local retries=20

				for retry in $(seq $retries); do

					if [ -f "/tmp/dhcp6probe" ]; then
						sleep 4
						break
					else
						sleep 1
						continue
					fi
				done

				link_to_portal=$(cat /tmp/dhcp6probe | grep -q -w "Got a valid ADVERTISE" ; echo $?)
				instances=$(pgrep odhcp6c | awk '{printf "%s ", $1}')

				for instance in $instances; do
					kill "$instance"
				done

				debugtype="debug"

				if [ "$link_to_portal" -eq 0 ]; then
					syslogmessage="portal detected"
				else
					syslogmessage="portal not detected"
				fi

				write_to_syslog
				cctimestamp=""
				scan_channel
				continue
			fi
		done
	fi
}

scan_channel() {
	debugtype="debug"
	syslogmessage="Entering scan channel...."
	write_to_syslog

	if [ "$link_to_portal" -gt 0 ]; then
		# No link to portal. Maybe we are on the wrong channel, so scan for meshid to get channel
		meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s " $1}')

		if [ ! -z "$meshconfigs" ]; then

			for meshconfig in $meshconfigs; do
				ifname=$(uci get $meshconfig.ifname)
				our_channel=$(iw dev $ifname info 2> /dev/null | grep -w "channel" | awk -F " " '{printf "%s", $2}')
				mesh_id=$(uci get $meshconfig.mesh_id)
				scanned_channels=$(iw dev $ifname scan 2> /dev/null | grep -F -w -B 20 "MESH ID: $mesh_id" | awk -F "primary channel: " '$2 != "" {printf "%s ",$2}')

				if [ ! -z "$our_channel" ] && [ "$our_channel" -le 14 ]; then
					band="2g"
				else
					band=""
				fi

				if [ ! -z "$our_channel" ] && [ ! -z "$scanned_channels" ]; then

					for channel in $scanned_channels; do

						if [ "$channel" -gt 14 ] && [ "$band" = "2g" ]; then
							continue
						fi

						if [ "$channel" -eq "$our_channel" ]; then
							continue
						else
							device=$(uci get $meshconfig.device)
							echo "set wireless.$device.channel='$channel'" | uci batch

							debugtype="notice"
							syslogmessage="Tracking channel from [ $our_channel ] to [ $channel ]"
							write_to_syslog

							restart_mesh
							break
						fi
					done
				fi

				ucibatch=""
			done

		fi
	fi
}

get_portal_state() {
	debugtype="debug"
	syslogmessage="In get_portal_state. firstloop=$firstloop..."
	write_to_syslog

	if [ "$firstloop" -eq 1 ]; then
		local retries=10

		for retry in $(seq $retries); do
			default_gw=$(ip route | grep "default via")

			if [ -z "$default_gw" ]; then
				debugtype="debug"
				syslogmessage="In Startup - portal upstream link check - iteration [ $retry ] ...."
				write_to_syslog
				sleep 1
				continue
			fi
		done

	else
		default_gw=$(ip route | grep "default via")
	fi


	debugtype="debug"
	syslogmessage="default_gw=$default_gw...."
	write_to_syslog

	authoritative=$(uci get dhcp.@dnsmasq[0].authoritative 2>/dev/null | awk '{printf "%d", $1}')
	config_ipaddr=$(uci get network.$auto_mesh_network.ipaddr)

	if [ -z "$authoritative" ]; then
		authoritative=2 # Meaning not yet set
	fi

	if [ -z "$default_gw" ]; then
		is_portal=""
		gw_ip=""
		gwstatus=""
	else
		gw_ip=$(echo "$default_gw" | awk -F" " '{printf "%s", $3}')

		if [ -z "$gw_ip" ] || [ "$config_ipaddr" = "$gw_ip" ]; then
			is_portal=""
		else
			# must test if upstream gateway is actually up
			# Trigger arp with a ping:
			gwstatus=$(ping -q -c 2 -W 1 "$gw_ip" &>/dev/null; ip neigh | grep "$gw_ip" | awk '{printf "%s", $NF}')

			if [ -z "$gwstatus" ]; then
				is_portal=""
			elif [ "$gwstatus" = "REACHABLE" ] || [ "$gwstatus" = "DELAY" ] || [ "$gwstatus" = "STALE" ]; then
				is_portal=$gwstatus
			elif [ "$gwstatus" = "PROBE" ] || [ "$gwstatus" = "INCOMPLETE" ] || [ "$gwstatus" = "FAILED" ]; then
				is_portal=""
			fi
		fi
	fi

	debugtype="debug"
	syslogmessage="leaving get_portal_state - is_portal=$is_portal.. gwstatus=$gwstatus.. gw_ip=$gw_ip.."
	write_to_syslog

}

restore_ipv4() {
	. /tmp/devicemac
	config_ipaddr=$(uci get network.$auto_mesh_network.ipaddr)
	active_ipaddr=$(ip addr show dev $device | grep -w "$config_ipaddr" | awk '{printf "%s", $2}' | awk -F"/" '{printf "%s", $1}')

	if [ "$config_ipaddr" != "$active_ipaddr" ]; then
		config_netmask=$(uci get network.$auto_mesh_network.netmask)
		cdir_netmask=$(echo "$config_netmask" | awk -F"." '{for(i=1; i<=NF; ++i) {mask+=8-log(2**8-$i)/log(2)} {printf "/%.0f", mask}}')
		cdir_ip="$config_ipaddr""$cdir_netmask"

		eval $(echo "$config_ipaddr" | awk -F"." '{printf "i1=%s i2=%s i3=%s i4=%s", $1 ,$2, $3, $4}')
		eval $(echo "$config_netmask" | awk -F"." '{printf "m1=%s m2=%s m3=%s m4=%s", $1 ,$2, $3, $4}')
		network_address="$(( $i1 & $m1 )).$(( $i2 & $m2 )).$(( $i3 & $m3 )).$(( $i4 & $m4 ))"
		eval $(echo "$network_address" | awk -F"." '{printf "n1=%s n2=%s n3=%s n4=%s", $1 ,$2, $3, $4}')
		broadcast_mask="$(( 255 ^ $m1 )).$(( 255 ^ $m2 )).$(( 255 ^ $m3 )).$(( 255 ^ $m4 ))"
		eval $(echo "$broadcast_mask" | awk -F"." '{printf "b1=%s b2=%s b3=%s b4=%s", $1 ,$2, $3, $4}')
		broadcast_address="$(( $n1 ^ $b1 )).$(( $n2 ^ $b2 )).$(( $n3 ^ $b3 )).$(( $n4 ^ $b4 ))"

		ip addr add "$cdir_ip" broadcast "$broadcast_address" dev "$device"
		ip link set "$device" arp on
		ip link set "$device" multicast on
	fi
}

check_portal() {
	is_portal=""
	debugtype="debug"
	syslogmessage="Entering check portal.... $auto_mesh_network"
	write_to_syslog

	if [ "$enabled" -eq 1 ]; then
		get_portal_state
		. /tmp/devicemac

		if [ -z "$is_portal" ]; then
			# This IS NOT a layer 3 mesh portal
			uci set mesh11sd.mesh_params.mesh_connected_to_as='0'

			if [ ! -f "/var/run/udhcpc-$device.pid" ]; then

				hostname=$(uci get system.@system[0].hostname 2>/dev/null)

				# Start the dhcp client
				dhcpstatus=$(udhcpc -p /var/run/udhcpc-$device.pid -b -t 3 -i $device -x hostname:$hostname -C -R -O 121)
				echo "$dhcpstatus" | grep "setting default routers"
				dhcptest=$?

				if [ "$dhcptest" -eq 0 ]; then
					debugtype="debug"
					syslogmessage="Remote dhcpv4 server found"
					write_to_syslog
				fi
			fi

			if [ "$authoritative" -eq 1 ] || [ "$authoritative" -eq 2 ]; then
				debugtype="debug"
				syslogmessage="This meshnode is NOT a portal"
				write_to_syslog

				uci set dhcp.@dnsmasq[0].authoritative='0'
				echo "set dhcp.$auto_mesh_network.ignore='1'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv6='disabled'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv4='disabled'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra='disabled'" | uci batch
				uci set dhcp.@dnsmasq[0].logfacility='-'
				uci set dhcp.@dnsmasq[0].quietdhcp='1'

				non_portal_mesh_hwmp_rootmode="2"
				echo "set mesh11sd.mesh_params.mesh_hwmp_rootmode='$non_portal_mesh_hwmp_rootmode'" | uci batch

				/sbin/service dnsmasq restart
				/sbin/service odhcpd restart

				local retries=20

				for retry in $(seq $retries); do
					default_gw=$(ip route | grep "default via")

					if [ -z "$default_gw" ]; then
						debugtype="debug"
						syslogmessage="Waiting for ipv4 default gateway - iteration [ $retry ] ...."
						write_to_syslog
						sleep 1
						continue
					else
						gw_ip=$(echo "$default_gw" | awk -F" " '{printf "%s", $3}')
						break
					fi
				done

				if [ ! -z "$gw_ip" ]; then
					echo "add_list dhcp.@dnsmasq[0].server='$gw_ip'" | uci batch
				else
					uci set dhcp.@dnsmasq[0].authoritative='2'
				fi

				/sbin/service dnsmasq reload
				/sbin/service system restart
				restart_mesh
			fi

			check_channel

		else
			# This IS a layer 3 mesh portal
			uci set mesh11sd.mesh_params.mesh_connected_to_as='1'

			if [ -f "/var/run/udhcpc-$device.pid" ]; then
				kill $(cat /var/run/udhcpc-$device.pid)
			fi

			restore_ipv4

			if [ "$authoritative" -eq 0 ] || [ "$authoritative" -eq 2 ]; then
				debugtype="debug"
				syslogmessage="This meshnode is an upstream portal"
				write_to_syslog

				uci set dhcp.@dnsmasq[0].authoritative='1'
				echo "set dhcp.$auto_mesh_network.ignore='0'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv6='server'" | uci batch
				echo "set dhcp.$auto_mesh_network.dhcpv4='server'" | uci batch
				echo "set dhcp.$auto_mesh_network.ra='server'" | uci batch
				uci set dhcp.@dnsmasq[0].logfacility='-'
				uci set dhcp.@dnsmasq[0].quietdhcp='1'
				echo "del_list dhcp.@dnsmasq[0].server='$config_ipaddr'" | uci batch
				echo "set mesh11sd.mesh_params.mesh_hwmp_rootmode='$mesh_hwmp_rootmode'" | uci batch

				/sbin/service dnsmasq restart
				/sbin/service odhcpd restart
				/sbin/service system restart
				restart_mesh
				if [ "$daemon_startup" -eq 1 ]; then
					daemon_startup=0
				fi
			fi
		fi
	fi

	debugtype="debug"
	syslogmessage="Leaving check portal...."
	write_to_syslog

}

check_hwmp() {
	get_mesh_iflist

	for iface in $iflist; do
		mesh_hwmp_active_path_timeout=$(iw dev $iface get mesh_param mesh_hwmp_active_path_timeout | awk '{printf "%s", $1}')
		mesh_hwmp_active_path_to_root_timeout=$(iw dev $iface get mesh_param mesh_hwmp_active_path_to_root_timeout | awk '{printf "%s", $1}')
		mesh_hwmp_rann_interval=$(iw dev $iface get mesh_param mesh_hwmp_rann_interval | awk '{printf "%s", $1}')
		mesh_hwmp_root_interval=$(iw dev $iface get mesh_param mesh_hwmp_root_interval | awk '{printf "%s", $1}')

		if [ "$mesh_hwmp_rann_interval" -gt "$mesh_hwmp_active_path_timeout" ]; then
			debugtype="warn"
			syslogmessage="mesh_hwmp_rann_interval is too large, reducing...."
			write_to_syslog
			echo "set mesh11sd.mesh_params.mesh_hwmp_rann_interval='$mesh_hwmp_active_path_timeout'" | uci batch
		fi

		if [ "$mesh_hwmp_root_interval" -gt "$mesh_hwmp_active_path_to_root_timeout" ]; then
			debugtype="warn"
			syslogmessage="mesh_hwmp_root_interval is too large, reducing...."
			write_to_syslog
			echo "set mesh11sd.mesh_params.mesh_hwmp_root_interval='$mesh_hwmp_active_path_to_root_timeout'" | uci batch
		fi

	done
}

wait_for_nodes() {
	local tics=5

	echo -n " Waiting for node list to build "

	ccpy_setup

	for tic in $(seq $tics); do
		ndp_scan_peer
		echo -n "* "
	done

	echo
}

convert_to_la() {
	local mac_to_convert="$1"
	eval $(echo "$mac_to_convert" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')
	la_octet=$(printf '%x\n' "$(( 0x2 | 0x$p1 ))")
	mac_la="$la_octet:$p2:$p3:$p4:$p5:$p6"
}

convert_from_la() {
	local mac_to_convert="$1"
	eval $(echo "$mac_to_convert" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')
	la_octet=$(printf '%x\n' "$(( 0x2 ^ 0x$p1 ))")
	mac_from_la="$la_octet:$p2:$p3:$p4:$p5:$p6"
}

make_indexed_mac() {
	local mac_to_convert="$1"
	local index="$2"
	eval $(echo "$mac_to_convert" | awk -F":" '{printf "p1=%s p2=%s p3=%s p4=%s p5=%s p6=%s", $1 ,$2, $3, $4, $5, $6}')
	indexed_octet=$(printf '%x\n' "$(( 0x$index + 0x$p4 ))")
	local len=${#indexed_octet}
	indexed_octet=${indexed_octet:$(($len-2)):2}

	if [ "$len" -eq 1 ]; then
		indexed_octet="0$indexed_octet"
	fi

	mac_indexed="$p1:$p2:$p3:$indexed_octet:$p5:$p6"
}


wait_for_mesh() {
	fixup1=0
	get_mesh_iflist

	local retries=2

	for iface in $iflist; do

		for retry in $(seq $retries); do
			wait_for_interface "$iface"

			if [ "$ifstatus" = "down" ]; then
				sleep 1
				continue
			fi
		done

		if [ "$ifstatus" = "down" ] && [ "$fixup1" -eq 0 ]; then
			fixup1=1
			debugtype="err"
			syslogmessage="First attempt at fixup: Mesh interface $iface failed - Possible wireless driver bug...."
			write_to_syslog

			. /tmp/devicemac
			convert_to_la "$devicemac"

			uci_section_name=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "%s", $2}')
			radio=$(uci show wireless | grep "$uci_section_name" | grep "device=" | awk -F "'" '{printf "%s", $2}')
			wifiifaces=$(uci show wireless | grep "device='$radio'" | awk -F "." '{printf "%s ", $2}')

			for wifiiface in $wifiifaces; do

				if [ "$wifiiface" = "$uci_section_name" ]; then
					continue
				else
					debugtype="notice"
					syslogmessage="Attempting fixup: Setting locally administered mac address"
					write_to_syslog
					sleep 1
					echo "set wireless.$wifiiface.macaddr='$mac_la'" | uci batch
					wifi
				fi
			done

		elif [ "$ifstatus" = "down" ] && [ "$fixup1" -eq 1 ]; then
			fixup1=2
			debugtype="err"
			syslogmessage="Second attempt at fixup: Mesh interface $iface failed - Possible wireless driver bug...."
			write_to_syslog
			uci_section_name=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "%s", $2}')
			radio=$(uci show wireless | grep "$uci_section_name" | grep "device=" | awk -F "'" '{printf "%s", $2}')
			wifiifaces=$(uci show wireless | grep "device='$radio'" | awk -F "." '{printf "%s ", $2}')

			for wifiiface in $wifiifaces; do

				if [ "$wifiiface" = "$uci_section_name" ]; then
					continue
				else
					debugtype="notice"
					syslogmessage="Attempting fixup: Disabling mesh gate for this node"
					write_to_syslog
					sleep 1
					echo "set wireless.$wifiiface.disabled='1'" | uci batch
					wifi
				fi
			done
		fi

		###########
		# Process htmode if needed
		#uci_section_name=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "%s", $2}')
		#radio=$(uci show wireless | grep "$uci_section_name" | grep "device=" | awk -F "'" '{printf "%s", $2}')

		#htmode_config=$(echo "get wireless.$radio.htmode" | uci batch)
		#echo "iface=$iface! uci_section_name=$uci_section_name! radio=$radio! wifiifaces=$wifiifaces! htmode_config=$htmode_config!"
		###########
	done
}

all_nodes_rssi_update() {

	if [ -f "/tmp/meshinterface" ]; then

		if [ ! -f "/tmp/mesh_rssi_threshold" ]; then
			debugtype="debug"
			syslogmessage="Disconnecting nodes"
			write_to_syslog

			rm /tmp/meshinterface

			get_wiphys

			if [ ! -z "$wiphys" ]; then

				for wiphy in $wiphys; do
					mesh_capable=$(iw phy $wiphy info | grep -A 15 "Supported interface modes:" | grep -w "mesh point")

					if [ -z "$mesh_capable" ]; then
						continue
					fi

					phyindex=$(echo "$wiphy" | awk -F "phy" '{printf "%s", $2}')
					phyindex=$(printf "%x" $phyindex)

					echo "set wireless.m11s$phyindex.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
				done
			fi

			restart_mesh
		fi
	fi
}

get_peers() {
	get_mesh_iflist

	for miface in $iflist; do
		wait_for_interface "$miface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		peers=$(iw dev $miface mpath dump | grep -c -w $miface)
		macrouting=$(iw dev $miface mpath dump | awk -F" " 'NR>1 {printf "%s/%s ",$1,$2}')

		for peer in $macrouting; do
			peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
			peerlist="$peerlist ""$peermac"
			convert_from_la "$peermac"
			peerlist="$peerlist ""$mac_from_la"
		done
	done
}

ccpy_setup() {
	get_peers

	meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s " $1}')
	connectlist=""

	if [ ! -z "$meshconfigs" ]; then
		for meshconfig in $meshconfigs; do
			networks=$(uci get $meshconfig.network)

			for network in $networks; do
				device=$(uci get network.$network.device)

				if [ "$network" != "$network_previous" ]; then
					connectlist="$connectlist ""$(ip -6 neigh | grep "fe80::" | grep "$device" | grep "lladdr" | awk '{printf "%s%s%s,%s ", $1, "%", $3, $5}')"
				fi

				network_previous=$network
			done

			# Stop after the first mesh network - there should only be one but more might have been added in error
			break
		done
	fi
}

ndp_scan_peer() {
	# NDP - Send linklocal multicast
	if [ -f "/tmp/devicemac" ]; then
		. /tmp/devicemac
		response=$(ping -q -c 2 -I "$device" "ff02::1" &>/dev/null)
	fi
}

get_wiphys() {
	# get list of physical wireless interfaces and reverse the list order
	wiphys=$(iw list | grep -w "Wiphy" | awk -F "Wiphy " '{printf "%s ", $2}' | awk '{ for (i = NF; i > 0; i = i - 1) printf("%s ", $i) }')
}

refresh_bridgemac() {
	# get the device device mac and write it to /tmp/devicemac
	device=$(uci get network.$auto_mesh_network.device)
	wait_for_interface "$device"
	devicemac=$(ip link | grep -A 1 "$device:" | grep -w "link/ether" | awk '{printf "%s", $2}')
	echo "device=\"$device\" ; devicemac=\"$devicemac\"" > /tmp/devicemac
}

auto_config() {
	debugtype="debug"
	syslogmessage="Entering auto config...."
	write_to_syslog
	autoconfig=0
	dfs_etsi="GB"

	# Are we using ath10k drivers?
	ath10k_ct=$(opkg list-installed | grep "ath10k"| grep -c "\-ct")

	if [ "$ath10k_ct" -gt 0 ]; then
		debugtype="err"
		syslogmessage="Unable to autoconfigure. Please install ath10k NON ct drivers for mesh support"
		write_to_syslog
		exit 1
	fi

	if [ "$autoconfig" -eq 0 ]; then

		# do we have a mesh supporting wpad?
		installed_wpad=$(opkg list-installed | grep "wpad" | awk '{printf "%s", $1}' | awk -F "-" '{print $2}')

		if [ "$installed_wpad" = "mesh" ] || [ "$installed_wpad" = "mbedtls" ] || [ "$installed_wpad" = "wolfssl" ] || [ "$installed_wpad" = "openssl" ]; then

			refresh_bridgemac
			. /tmp/devicemac
			get_wiphys

			if [ ! -z "$wiphys" ]; then
				for wiphy in $wiphys; do
					mesh_capable=$(iw phy $wiphy info | grep -A 15 "Supported interface modes:" | grep -w "mesh point")

					if [ -z "$mesh_capable" ]; then
						continue
					fi

					# Prerequisites met so we can autoconfigure
					phyindex=$(echo "$wiphy" | awk -F "phy" '{printf "%s", $2}')

					phyindex=$(printf "%x" $phyindex)
					mesh_ifname="$mesh_basename""$phyindex"


					echo "set wireless.m11s$phyindex=wifi-iface" | uci batch
					echo "set wireless.m11s$phyindex.device='radio$phyindex'" | uci batch
					echo "set wireless.m11s$phyindex.mode='mesh'" | uci batch
					echo "set wireless.m11s$phyindex.encryption='sae'" | uci batch
					echo "set wireless.m11s$phyindex.mesh_id='$auto_mesh_id'" | uci batch
					echo "set wireless.m11s$phyindex.key='$auto_mesh_key'" | uci batch
					echo "set wireless.m11s$phyindex.network='$auto_mesh_network'" | uci batch
					echo "set wireless.m11s$phyindex.ifname='$mesh_ifname'" | uci batch
					echo "set wireless.m11s$phyindex.mesh_rssi_threshold='$mesh_rssi_threshold'" | uci batch
					echo "set wireless.radio$phyindex.disabled='0'" | uci batch


					country=$(uci get wireless.radio$phyindex.country 2> /dev/null | awk '{printf "%s", $1}')

					if [ -z "$country" ]; then
						echo "set wireless.radio$phyindex.country='$dfs_etsi'" | uci batch
						syslogmessage="WARNING - country code not set - interoperability with other mesh nodes may be compromised or fail altogether...."
						debugtype="warn"
						write_to_syslog
						syslogmessage="WARNING - country code defaulting to [ DFS-ETSI ]...."
						debugtype="warn"
						write_to_syslog
						country="$dfs_etsi"
					fi

					band=$(uci get wireless.radio$phyindex.band 2> /dev/null | awk '{printf "%s", $1}')

					if [ "$auto_mesh_band" = "2g40" ]; then
						mesh_band="2g"
					else
						mesh_band=$auto_mesh_band
					fi

					if [ "$band" = "$mesh_band" ]; then
						echo "set wireless.m11s$phyindex.disabled='0'" | uci batch
					else
						echo "set wireless.m11s$phyindex.disabled='1'" | uci batch
					fi

					if [ "$band" = "2g" ]; then
						get_portal_state

						if [ "$auto_mesh_band" = "2g40" ]; then
							originalhtmode=$(echo "get wireless.radio$phyindex.htmode" | uci batch)
							he_mode=$(echo "$originalhtmode" | grep -q "HE"; echo $?)

							if [ "$he_mode" -eq 0 ]; then
								htmode="HE40"
							else
								htmode="HT40"
							fi

							if [ ! -z "$country" ]; then
								echo "set wireless.radio$phyindex.noscan='1'" | uci batch
								echo "set wireless.radio$phyindex.htmode='$htmode'" | uci batch
							else
								echo "set wireless.radio$phyindex.noscan='0'" | uci batch
								echo "set wireless.radio$phyindex.htmode='$originalhtmode'" | uci batch
							fi

						fi

						if [ ! -z "$is_portal" ] || [ "$portal_detect" -eq 0 ]; then

							if [ "$portal_channel" = "auto" ] && [ "$band" = "2g" ]; then

								for i in $(seq 9 1 64); do
									#choose start point of sequence away from end eg "9"
									chan=$((0x$(printf "$devicemac" | sha256sum | awk '{printf $1}' | tail -c$i | head -c1)))

									#Check for max
									if [ "$chan" -gt 13 ]; then
										continue
									fi

									#Check for min
									if [ "$chan" -le 0 ]; then
										continue
									fi

									echo "set wireless.radio$phyindex.channel='$chan'" | uci batch
									break
								done

							elif [ "$portal_channel" != "auto" ] && [ "$portal_channel" != "default" ] && [ "$band" = "2g" ]; then
								chan="$portal_channel"

								#Check for max
								if [ "$chan" -gt 13 ]; then
									chan=13
								fi

								#Check for min
								if [ "$chan" -le 0 ]; then
									chan=1
								fi

								echo "set wireless.radio$phyindex.channel='$chan'" | uci batch
							fi
						fi
					fi
				done

				#Add suffix to ssid
				if [ "$ssid_suffix_enable" -eq 1 ]; then
					aplist=$(uci show wireless 2> /dev/null | grep "='ap'" | awk -F "." '{printf "%s.%s ", $1, $2}')
					suffix=$(echo $devicemac | awk -F":" '{printf "%s%s", $5, $6}')

					for ap in $aplist; do
						radio=$(uci get "$ap.device" 2> /dev/null)
						band=$(uci get "wireless.$radio.band" 2> /dev/null)
						ssid=$(uci get "$ap.ssid" 2> /dev/null | awk '{printf "%s", $1}')
						echo "set $ap.ssid='$ssid-$band-$suffix'" | uci batch
					done

					echo "set system.@system[0].hostname='meshnode-$suffix'" | uci batch
					/sbin/service system restart
				fi

				debugtype="debug"
				syslogmessage="auto config complete...."
				write_to_syslog

				# set changed flag to signify a mesh restart is needed
				changed=1

			else
				debugtype="err"
				syslogmessage="Unable to autoconfigure. No physical wireless interfaces found"
				write_to_syslog
			fi
		else
			debugtype="err"
			syslogmessage="Unable to autoconfigure. Please install package wpad-mesh-mbedtls, wpad-mesh-wolfssl, wpad-mesh-openssl or an equivalent full version"
			write_to_syslog
			exit 1
		fi

		# TODO If we want to call auto_config from cli then we can stop it happening more than once by uncommenting the following:
		#autoconfig=1
	fi
}

urlencode() {
	entitylist="
		s/%/%25/g
		s/\s/%20/g
		s/\"/%22/g
		s/:/%3A/g
		s/>/%3E/g
		s/</%3C/g
		s/'/%27/g
		s/\`/%60/g
	"
	local buffer="$1"

	for entity in $entitylist; do
		urlencoded=$(echo "$buffer" | sed "$entity")
		buffer=$urlencoded
	done

	urlencoded=$(echo "$buffer" | awk '{ gsub(/\$/, "\\%24"); print }')
}

urldecode() {
	entitylist="
		s/%22/\"/g
		s/%3A/:/g
		s/%3E/>/g
		s/%3C/</g
		s/%27/'/g
		s/%60/\`/g
		s/%25/%/g
	"
	local buffer="$1"

	for entity in $entitylist; do
		urldecoded=$(echo "$buffer" | sed "$entity")
		buffer=$urldecoded
	done

	buffer=$(echo "$buffer" | awk '{ gsub(/%24/, "$"); print }')
	urldecoded=$(echo "$buffer" | awk '{ gsub(/%20/, " "); print }')
}

arp_setup() {
	refresh_bridgemac
	echo 1 > /proc/sys/net/ipv4/conf/all/arp_accept
	echo 1 > /proc/sys/net/ipv4/conf/default/arp_accept
	echo 1 > /proc/sys/net/ipv4/conf/$device/arp_accept
	echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
	echo 2 > /proc/sys/net/ipv4/conf/default/arp_announce
	echo 2 > /proc/sys/net/ipv4/conf/$device/arp_announce
	echo 0 > /proc/sys/net/ipv4/conf/all/arp_filter
	echo 0 > /proc/sys/net/ipv4/conf/default/arp_filter
	echo 0 > /proc/sys/net/ipv4/conf/$device/arp_filter
	echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
	echo 0 > /proc/sys/net/ipv4/conf/default/arp_ignore
	echo 0 > /proc/sys/net/ipv4/conf/$device/arp_ignore
	echo 1 > /proc/sys/net/ipv4/conf/all/arp_notify
	echo 1 > /proc/sys/net/ipv4/conf/default/arp_notify
	echo 1 > /proc/sys/net/ipv4/conf/$device/arp_notify
	echo 1 > /proc/sys/net/ipv4/conf/all/proxy_arp
	echo 1 > /proc/sys/net/ipv4/conf/default/proxy_arp
	echo 1 > /proc/sys/net/ipv4/conf/$device/proxy_arp
	echo 1 > /proc/sys/net/ipv4/conf/all/proxy_arp_pvlan
	echo 1 > /proc/sys/net/ipv4/conf/default/proxy_arp_pvlan
	echo 1 > /proc/sys/net/ipv4/conf/$device/proxy_arp_pvlan
	echo 1 > /proc/sys/net/ipv4/conf/all/bc_forwarding
	echo 1 > /proc/sys/net/ipv4/conf/default/bc_forwarding
	echo 1 > /proc/sys/net/ipv4/conf/$device/bc_forwarding

	echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra
	echo 2 > /proc/sys/net/ipv6/conf/default/accept_ra
	echo 2 > /proc/sys/net/ipv6/conf/$device/accept_ra
	echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
	echo 1 > /proc/sys/net/ipv6/conf/default/forwarding
	echo 1 > /proc/sys/net/ipv6/conf/$device/forwarding
	echo 1 > /proc/sys/net/ipv6/conf/all/proxy_ndp
	echo 1 > /proc/sys/net/ipv6/conf/default/proxy_ndp
	echo 1 > /proc/sys/net/ipv6/conf/$device/proxy_ndp
	echo 5 > /proc/sys/net/ipv6/conf/all/router_solicitations
	echo 5 > /proc/sys/net/ipv6/conf/default/router_solicitations
	echo 5 > /proc/sys/net/ipv6/conf/$device/router_solicitations


}
check_path_changes() {
	# get list of interfaces
	get_mesh_iflist

	if [ -f "/tmp/devicemac" ]; then
		. /tmp/devicemac
	fi

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		# get list of mesh parameters for this interface
		get_params

		if [ -z "$params" ]; then
			# this is not a mesh interface
			continue
		else
			macrouting=$(iw dev $iface mpath dump 2>/dev/null | awk -F" " 'NR>1 {printf "%s/%s/%s/%s/%s ",$1,$2,$5,$11,$12}')

			if [ ! -z "$macrouting" ]; then
				for peer in $macrouting; do
					peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
					peermac_id=$(echo -n "$peermac" | sha256sum | awk '{printf $1}')

					next_hop=$(echo "$peer" | awk -F"/" '{printf "%s", $2}')

					hop_count=$(echo "$peer" | awk -F"/" '{printf "%s", $4}')

					path_change_count=$(echo "$peer" | awk -F"/" '{printf "%s", $5}')

					metric=$(echo "$peer" | awk -F"/" '{printf "%s", $3}')

					if [ -f "/tmp/$peermac_id" ]; then
						. /tmp/$peermac_id
					else
						previous_path_change_count=1
					fi

					if [ "$path_change_count" -ne "$previous_path_change_count" ]; then

						if [ "$hop_count" -gt 1 ] && [ "$path_change_count" -gt 1 ]; then

							is_connected=$(iw dev $iface station dump | grep -q "$peermac"; echo -n $?)

							if [ "$is_connected" -eq 0 ]; then
								debugtype="warn"
								syslogmessage="Warning! Station [ $peermac ] is [ $hop_count ] hops away and [ $path_change_count ] path_change(s) for station have been detected"
								write_to_syslog
								syslogmessage="Warning! Station [ $peermac ], consider its location or adjust txpower and/or rssi_threshold"
								write_to_syslog
								# If we want to make reactive fixups, do them here, for example:
								#iw dev $iface station del $peermac
								#iw dev $iface mpath del $peermac
							fi

						elif [ "$hop_count" -eq 1 ] && [ "$path_change_count" -gt 1 ]; then
							debugtype="warn"
							syslogmessage="Warning! Station [ $peermac ] is an immediate neighbour, but has had [ $path_change_count ] path_change(s) detected"
							write_to_syslog
							syslogmessage="Warning! Station [ $peermac ], consider its location or adjust txpower and/or rssi_threshold"
							write_to_syslog
							# If we want to make reactive fixups, do them here, for example:
							#iw dev $iface mpath del $peermac
						fi

						echo "previous_path_change_count=$path_change_count" > /tmp/$peermac_id
					else
						debugtype="info"
						syslogmessage="Path to station [ $peermac ] is stable"
						write_to_syslog
					fi
				done
			fi
		fi
	done
}

block_bridge_loops() {

	table_ruleset=$(nft -a list table bridge mesh11s 2> /dev/null)

	if [ "$?" -gt 0 ]; then
		# Create the table
		nft add table bridge mesh11s
		# Create the chain
		nft add chain bridge mesh11s mesh_PRE { type filter hook prerouting priority -350\; }
	fi

	. /tmp/devicemac
	local br_iface
	local br_ifaces=$(brctl showstp "$device" | grep -B 1 -w "port id" | grep "(" | awk '{printf "%s ", $1}')

	get_mesh_iflist
	br_mac_list=$devicemac
	local index=0

	for br_iface in $br_ifaces; do
		rulecount=$(nft -a list table bridge mesh11s 2> /dev/null | grep -w -c "$br_iface" | awk '{printf "%s", $1}')

		if [ "$rulecount" -lt 2 ]; then
			rule_status=$(nft add rule bridge mesh11s mesh_PRE meta iifname { $br_iface } ether saddr "\"$devicemac\"" counter drop; echo -n $?)
			check_rule_status
			rule_status=$(nft add rule bridge mesh11s mesh_PRE meta iifname { $br_iface } counter accept; echo -n $?)
			check_rule_status
		fi

		index=$(($index + 1))
		convert_to_la "$devicemac"
		is_meshif=$(echo "$iflist" | grep -q "$br_iface"; echo -n $?)

		if [ $is_meshif -ne 0 ]; then
			# This is not a mesh interface
			local br_iface_mac=$(ip link | grep -A 1 "$br_iface" | grep "link/ether" | awk 'NR==1 {printf "%s", $2}')
			is_same=$(echo "$br_mac_list" | grep -q "$br_iface_mac"; echo -n $?)

			if [ "$is_same" -eq 0 ]; then
				#This is a duplicate mac address so change it
				# get la

				# increment by index
				#Now not a duplicate so save it in the list
				br_mac_list="$br_mac_list $br_iface_mac"

				#change mac of iface

				make_indexed_mac "$mac_la" "$index"
				ip link set $br_iface down; ip link set $br_iface up address $mac_indexed
			else
				#Not a duplicate so save it in the list
				br_mac_list="$devicemac $br_iface_mac"
			fi
		fi

	done

}

check_rule_status() {

	if [ "$rule_status" -gt 0 ]; then
		debugtype="warn"
		syslogmessage="ERROR adding nftables rule - error code [ $rule_status ] Is kmod-nft-bridge package installed?"
		write_to_syslog
		debugtype="warn"
		syslogmessage="WARNING! A severe mesh bridge-loop storm is likely to occur if any non-mesh backhaul segments are used!"
		write_to_syslog
	fi
}

##############
# Start point
##############

if [ -f "/var/run/mesh11sd.pid" ]; then
	mesh11sdpid=$(cat /var/run/mesh11sd.pid)
else
	mesh11sdpid=$(pgrep -f "/bin/sh /usr/sbin/mesh11sd")
fi

get_current_setup 2> /dev/null

if [ -z "$1" ] || [ "$1" = "-h" ] || [ $1 = "--help" ] || [ $1 = "help" ]; then
	echo "
  Usage: mesh11sd [option] [argument...]]

  Option: -h --help help
  Returns: This help information

  Option: -v --version version
  Returns: The mesh11sd version

  Option: debuglevel
  Argument: 0 - silent, 1 - notice, 2 - info, 3 - debug
  Returns: The mesh11sd debug level

  Option: enable
  Returns: \"1\" and exit code 0 if successful, exit code 1 if was already enabled

  Option: disable
  Returns: \"0\" and exit code 0 if successful, exit code 1 if was already disabled

  Option: status
  Returns: the mesh status in json format

  Option: connect
  Connect a remote terminal session on a remote meshnode
  Usage: mesh11sd connect [remote_meshnode_macaddress]
    If the remote meshnode mac address is omitted, a list of meshnode mac addresses available for connection is listed

  Option: copy
  Copy a file to /tmp on a remote meshnode
  Usage: mesh11sd copy [remote_meshnode_macaddress] [path_of_source_file]
    If the remote meshnode mac address is null, or both arguments are omitted, a list of meshnode mac addresses available for copy is listed

  Option: txpower
  Change the mesh transmit power
  Usage: mesh11sd txpower [+|-]
    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm
    Takes effect immediately

  Option: stations
  List all mesh peer stations directly connected to this mesh peer station (one hop)
  Usage: mesh11sd stations

  Option: mesh_rssi_threshold
  Change the mesh rssi threshold
  Usage: mesh11sd mesh_rssi_threshold [+|-] [force]
    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm
    Takes effect immediately on NEW connections
    The keyword \"force\" forces the new threshold on all connected peers
    Warning - \"force\" will briefly remove this node from the mesh network, taking a few seconds to rejoin

  Option: commit_changes
  Usage: mesh11sd commit_changes
    Commits changes to txpower and rssi_threshold to non volatile configuration (make permanent)

  Option: opkg_force_ipv4
  Usage: mesh11sd opkg_force_ipv4
    Forces opkg to use ipv4 for its downloads

  Option: opkg_revert_to_default
  Usage: mesh11sd opkg_revert_to_default
    Reverts opkg to default for its downloads

  For further documentation, see: https://github.com/openNDS/mesh11sd#readme

"

elif [ "$1" = "-v" ] || [ $1 = "--version" ] || [ $1 = "version" ]; then
	echo "mesh11sd version $version"

elif [ "$1" = "debuglevel" ]; then

	if [ -z "$2" ];then
		echo "$debuglevel"
	else

		if [ "$2" -ge 0 ] && [ "$2" -le 3 ]; then
			ucibatch="set mesh11sd.setup.debuglevel='$2'"
			echo "$ucibatch" | uci batch
			echo "$2"
		else
			echo "Invalid debuglevel requested"
		fi
	fi

elif [ "$1" = "enable" ]; then

	if [ "$enabled" -eq 1 ]; then
		echo "1"
		exit 1
	else
		ucibatch="set mesh11sd.setup.enabled='1'"
		echo "$ucibatch" | uci batch
		echo "1"
		exit 0
	fi

elif [ "$1" = "disable" ]; then

	if [ "$enabled" -eq 0 ]; then
		echo "0"
		exit 1
	else
		ucibatch="set mesh11sd.setup.enabled='0'"
		echo "$ucibatch" | uci batch
		echo "0"
		exit 0
	fi

elif [ "$1" = "status" ]; then
	# get list of interfaces
	get_mesh_iflist

	if [ -f "/tmp/devicemac" ]; then
		. /tmp/devicemac
	fi

	procd_status=$(/etc/init.d/mesh11sd status)

	echo "{"
	echo "  \"setup\":{"
	echo "    \"version\":\"$version\","
	echo "    \"enabled\":\"$enabled\","
	echo "    \"procd_status\":\"$procd_status\","
	echo "    \"portal_detect\":\"$portal_detect\","
	echo "    \"portal_channel\":\"$portal_channel\","
	echo "    \"mesh_basename\":\"$mesh_basename\","
	echo "    \"auto_config\":\"$auto_config\","
	echo "    \"auto_mesh_network\":\"$auto_mesh_network\","
	echo "    \"auto_mesh_band\":\"$auto_mesh_band\","
	echo "    \"auto_mesh_id\":\"$auto_mesh_id\","
	echo "    \"mesh_gate_enable\":\"$mesh_gate_enable\","

	# get list of interfaces
	get_mesh_iflist

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		txpower=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		break
	done


	echo "    \"txpower\":\"$txpower\","
	echo "    \"mesh_path_cost\":\"$mesh_path_cost\","
	echo "    \"checkinterval\":\"$checkinterval\","
	echo "    \"interface_timeout\":\"$interface_timeout\","
	echo "    \"ssid_suffix_enable\":\"$ssid_suffix_enable\","
	echo "    \"debuglevel\":\"$debuglevel\""
	echo "  }"
	echo "  \"interfaces\":{"

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		# get list of mesh parameters for this interface
		get_params

		if [ -z "$params" ]; then
			# this is not a mesh interface
			continue
		else
			params=$(echo "$params" | awk -F" " '{printf "      \"%s\":\"%s\",\n", $1, $3}')
			echo "    \"$iface\":{"
			echo "$params"

			meshconfig=$(uci show wireless | grep "ifname='$iface'" | awk -F ".ifname='$iface'" '{printf "%s", $1}')

			if [ -z "$meshconfig" ]; then
				echo "      \"interface\":\"unmanaged\""
				echo "    }"
			else
				device=$(uci get $meshconfig.device 2> /dev/null)
				channel=$(uci show wireless | grep "$device.channel" | awk -F "'" '{printf "%s", $2}')
				meshid=$(uci get $meshconfig.mesh_id 2> /dev/null)

				echo "      \"mesh_id\":\"$meshid\","
				echo "      \"device\":\"$device\","
				echo "      \"channel\":\"$channel\","

				tx_packets=$(devstatus $iface 2>/dev/null | grep "tx_packets" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"tx_packets\":\"$tx_packets\","

				tx_bytes=$(devstatus $iface 2>/dev/null | grep "tx_bytes" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"tx_bytes\":\"$tx_bytes\","

				rx_packets=$(devstatus $iface 2>/dev/null | grep "rx_packets" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"rx_packets\":\"$rx_packets\","

				rx_bytes=$(devstatus $iface 2>/dev/null | grep "rx_bytes" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"rx_bytes\":\"$rx_bytes\","

				peernum=0
				peers=$(iw dev $iface mpath dump 2>/dev/null | grep -c -w $iface)
				macrouting=$(iw dev $iface mpath dump 2>/dev/null | awk -F" " 'NR>1 {printf "%s/%s/%s/%s/%s ",$1,$2,$5,$11,$12}')
				peer_macs="$devicemac ""$(iw dev m-11s-0 mpath dump 2>/dev/null | awk -F" " 'NR>1 {printf "%s ",$1}')"

				echo "      \"this_node\":\"$devicemac\","
				echo "      \"active_peers\":\"$peers\","
				echo "      \"peers\":{"

				for peer in $macrouting; do
					peernum=$((peernum+1))
					peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
					echo "        \"$peermac\":{"

					next_hop=$(echo "$peer" | awk -F"/" '{printf "%s", $2}')
					echo "          \"next_hop\":\"$next_hop\""

					hop_count=$(echo "$peer" | awk -F"/" '{printf "%s", $4}')
					echo "          \"hop_count\":\"$hop_count\""

					path_change_count=$(echo "$peer" | awk -F"/" '{printf "%s", $5}')
					echo "          \"path_change_count\":\"$path_change_count\""

					metric=$(echo "$peer" | awk -F"/" '{printf "%s", $3}')
					echo "          \"metric\":\"$metric\""

					if [ "$peers" = "$peernum" ]; then
						echo "        }"
					else
						echo "        },"
					fi
				done
				echo "      }"

				ap_ifaces=$(iw dev | grep -B 7 "type AP"| grep "Interface" | awk '{printf "%s ", $2}')

				for ap_iface in $ap_ifaces; do
					ap_sta=$(iw dev $ap_iface station dump | grep "Station" | awk  '{printf "%s ", $2}')
					ap_stas="$ap_stas""$ap_sta"
				done

				for ap_sta in $ap_stas; do
					starouting="$starouting""$ap_sta/$devicemac "
				done

				stanum=0
				realstanum=0
				stations=0
				starouting="$starouting"$(iw dev $iface mpp dump | awk -F" " 'NR>1 {printf "%s/%s ",$1,$2}')
				stas=$(iw dev $iface mpp dump | awk -F" " 'NR>1 {printf "%s ",$1}')
				stas="$ap_stas""$stas"

				for sta in $stas; do
					sta=$(printf "%s" "$sta" | awk -F ":" '{printf "%s:%s:%s:%s:%s", $2, $3, $4, $5, $6}')
					inpeermacs=$(echo "$peer_macs" | grep "$sta")

					if [ "$inpeermacs" = "$peer_macs" ]; then
						continue
					else
						stations=$((stations+1))
					fi
				done

				echo "      \"active_stations\":\"$stations\","
				echo "      \"stations\":{"

				for staroute in $starouting; do
					sta="${staroute%/*}"
					inpeermacs=$(echo "$peer_macs" | grep "$sta")

					if [ "$inpeermacs" = "$peer_macs" ]; then
						continue
					fi

					stanum=$((stanum+1))
					stamac=$(echo "$sta" | awk -F"/" '{printf "%s", $1}')
					proxy_node=$(echo "$sta" | awk -F"/" '{printf "%s", $2}')
					proxy_node="${staroute##*/}"

					stamacid=$(printf "%s" "$stamac" | awk -F ":" '{printf "%s%s%s%s%s", $2, $3, $4, $5, $6}')
					proxy_nodeid=$(printf "%s" "$proxy_node" | awk -F ":" '{printf "%s%s%s%s%s", $2, $3, $4, $5, $6}')

					if [ "$stamacid" = "$proxy_nodeid" ]; then
						continue
					fi

					realstanum=$((realstanum+1))
					echo "        \"$stamac\":{"
					echo "          \"proxy_node\":\"$proxy_node\""

					if [ "$stations" = "$stanum" ]; then
						echo "        }"
					else
						echo "        },"
					fi

				done
				echo "      }"
				echo "    }"
			fi
		fi
	done

	echo "  }"
	echo "}"

elif [ "$1" = "connect" ]; then

	if [ "$auto_config" -eq 0 ]; then
		echo "======================================================================"
		echo "Unable to connect -  enable auto_config to connect to other meshnodes"
		echo "======================================================================"
		exit 1
	fi

	nodeaddr=$(printf "%s" "$2" | tr "-" ":" | tr "[A-Z]" "[a-z]")

	echo "==========================================================================="

	echo " Connect a remote terminal session on a remote meshnode"
	echo "    Usage: mesh11sd connect [remote_meshnode_macaddress]"
	echo
	wait_for_nodes
	echo

	if [ -z "$nodeaddr" ]; then
		echo " If the node you are looking for is not in the list - re-run this command."
		echo "==========================================================================="
		echo " The following meshnodes are available for remote connection:"
	fi

	if [ ! -z "$peerlist" ]; then

		for connectto in $connectlist; do
			linklocal="${connectto%,*}"
			ndpmac="${connectto##*,}"

			ispeer=$(echo "$peerlist" | grep -q "$ndpmac"; echo $?)

			if [ "$ispeer" -ne 0 ]; then
				continue
			fi

			ndpmacf=$(printf "%s" "$ndpmac" | tr ":" "-")
			ip6addr=$(echo "$linklocal" | awk -F "%" '{printf "%s", $1}')

			if [ -z "$nodeaddr" ]; then
				echo "	$ndpmacf	[ ip address: $ip6addr ]"
			elif [ ! -z "$nodeaddr" ]; then

				if [ "$nodeaddr" = "$ndpmac" ]; then
					echo
					echo "Trying to connect to meshnode \"$ndpmacf\"....."
					ssh "root@$linklocal"
					echo
					echo "Disconnected from meshnode \"$ndpmacf\""
					echo
					break
				fi
			fi
		done
	fi

	echo "==========================================================================="
	echo

	exit 0

elif [ "$1" = "copy" ]; then

	if [ "$auto_config" -eq 0 ]; then
		echo "================================================================"
		echo "Unable to copy -  enable auto_config to copy to other meshnodes"
		echo "================================================================"
		exit 1
	fi
	nodeaddr=$(printf "%s" "$2" | tr "-" ":" | tr "[A-Z]" "[a-z]")
	filepath=$3
	filename=$(echo "$filepath" | awk -F "/" '{printf $NF}')

	echo "==========================================================================="

	echo " Copy a file to /tmp on a remote meshnode"
	echo "    Usage: mesh11sd copy [remote_meshnode_macaddress] [path_of_source_file]"
	echo
	wait_for_nodes
	echo

	if [ -z "$nodeaddr" ]; then
		echo " If the node you are looking for is not in the list - re-run this command."
		echo "==========================================================================="
		echo " The following meshnodes are available for remote copy:"

	fi

	if [ ! -z "$peerlist" ]; then

		for connectto in $connectlist; do
			linklocal="${connectto%,*}"
			ndpmac="${connectto##*,}"

			ispeer=$(echo "$peerlist" | grep -q "$ndpmac"; echo $?)

			if [ "$ispeer" -ne 0 ]; then
				continue
			fi

			ndpmacf=$(printf "%s" "$ndpmac" | tr ":" "-")
			ip6addr=$(echo "$linklocal" | awk -F "%" '{printf "%s", $1}')

			if [ -z "$nodeaddr" ]; then
				echo "	$ndpmacf	[ ip address: $ip6addr ]"
			elif [ ! -z "$nodeaddr" ]; then

				if [ "$nodeaddr" = "$ndpmac" ] && [ ! -z "$filepath" ]; then
					echo
					echo "Trying to copy to meshnode \"$ndpmacf\"....."
					scp "$filepath" "root@[$linklocal]:/tmp/$filename"
					echo
					echo "Disconnected from meshnode \"$ndpmacf\""
					echo
					break
				fi
			fi
		done
	fi

	echo "==========================================================================="
	echo

	exit 0

elif [ "$1" = "txpower" ]; then
	# get list of interfaces
	get_mesh_iflist
	new_current_power=""

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		current_power=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		break
	done

	echo "==========================================================================="

	echo "Change the mesh transmit power"

	if [ -z "$2" ]; then
		echo "    Current TX power [ $current_power ] dBm "
		echo "    Usage: mesh11sd txpower [+|-] "
		echo "    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm"

	elif [ "$2" = "+" ]; then
		echo "    Previous TX power [ $current_power ] dBm "
		new_power=$(($current_power + 3))
		iw dev $iface set txpower fixed "$(($new_power * 100))"
		new_current_power=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		echo
		echo "    New TX power - [ $new_current_power ] dBm "

	elif [ "$2" = "-" ]; then
		echo "    Previous TX power [ $current_power ] dBm "
		new_power=$(($current_power - 3))
		iw dev $iface set txpower fixed "$(($new_power * 100))"
		new_current_power=$(iwinfo | grep -A 7 "$iface" | grep -w "Tx-Power:" | awk '{printf "%s", $2}')
		echo
		echo "    New TX power - [ $new_current_power ] dBm "
	fi

	if [ ! -z "$new_current_power" ]; then
		echo "set mesh11sd.setup.txpower='$new_current_power'" | uci batch
	fi

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "stations" ]; then
	# get list of interfaces
	get_mesh_iflist

	echo "==========================================================================="

	echo "Stations connected to this node:"

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		iw dev $iface station dump
	done

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "commit_changes" ]; then

	changes=$(uci changes mesh11sd)
	changelist=$(echo "$changes" | awk '{printf "%s ", $1}')

	mesh_rssi_threshold_change=$(echo "$changes" | grep "mesh_rssi_threshold" | tail -n 1)
	txpower_change=$(echo "$changes" | grep "txpower" | tail -n 1)

	echo "==========================================================================="

	echo "Commit changes, making them non volatile on restart/reboot"

	if [ -z "$2" ] || [ "$2" != "commit" ]; then
		echo "    Usage: mesh11sd commit_changes commit "
		echo "    Writes changes to configuration file "
	elif [ "$2" = "commit" ]; then
		uci revert mesh11sd
		echo "set $mesh_rssi_threshold_change" | uci batch
		echo "set $txpower_change" | uci batch
		uci commit mesh11sd

		for change in $changelist; do
			is_txpower=$(echo "$change" | grep -q "txpower"; echo -n $?)
			is_mesh_rssi_threshold=$(echo "$change" | grep -q "mesh_rssi_threshold"; echo -n $?)

			if [ "$is_txpower" -eq 0 ] || [ "$is_mesh_rssi_threshold" -eq 0 ]; then
				continue
			fi

			echo "set $change" | uci batch

		done
	fi


	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "mesh_rssi_threshold" ]; then
	# get list of interfaces
	get_mesh_iflist

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		current_rssi=$(iw dev $iface get mesh_param mesh_rssi_threshold | awk '{printf "%s", $1}')
		break
	done

	echo "==========================================================================="

	echo "Change the mesh rssi threshold"

	if [ -z "$2" ]; then
		echo "    Current threshold for new connections [ $current_rssi ] dBm "
		echo "    Usage: mesh11sd mesh_rssi_threshold [+|-] [force]"
		echo "    where \"+\" increments by 3dBm and \"-\" decrements by 3dBm"
		echo "    The keyword \"force\" forces the new threshold on all peers"

	elif [ "$2" = "+" ]; then
		new_rssi=$(($current_rssi + 3))
		echo "    Previous threshold [ $current_rssi ] dBm "

		if [ -z "$3" ]; then
			touch /tmp/mesh_rssi_threshold
			iw dev $iface set mesh_param mesh_rssi_threshold $new_rssi
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			new_current_rssi=$(iw dev $iface get mesh_param mesh_rssi_threshold | awk '{printf "%s", $1}')

		elif [ "$3" = "force" ]; then
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			echo
			echo "    Forcing new threshold on all peers..."

			if [ -f "/tmp/mesh_rssi_threshold" ]; then
				rm /tmp/mesh_rssi_threshold
			fi

			new_current_rssi=$new_rssi
		fi

		echo
		echo "    New threshold - [ $new_current_rssi ] dBm "

	elif [ "$2" = "-" ]; then
		new_rssi=$(($current_rssi - 3))
		echo "    Previous threshold [ $current_rssi ] dBm "

		if [ -z "$3" ]; then
			touch /tmp/mesh_rssi_threshold
			iw dev $iface set mesh_param mesh_rssi_threshold $new_rssi
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			new_current_rssi=$(iw dev $iface get mesh_param mesh_rssi_threshold | awk '{printf "%s", $1}')

		elif [ "$3" = "force" ]; then
			echo "set mesh11sd.mesh_params.mesh_rssi_threshold='$new_rssi'" | uci batch
			echo
			echo "    Forcing new threshold on all peers..."

			if [ -f "/tmp/mesh_rssi_threshold" ]; then
				rm /tmp/mesh_rssi_threshold
			fi

			new_current_rssi=$new_rssi
		fi

		echo
		echo "    New threshold - [ $new_current_rssi ] dBm "
	fi

	echo "==========================================================================="
	echo
	exit 0

elif [ "$1" = "opkg_force_ipv4" ]; then

	if ! grep "#!/bin/sh" /usr/bin/wget; then
		mv /usr/bin/wget /usr/bin/wget.bak
		echo -e "#!/bin/sh\n/usr/bin/wget.bak -4 \"\$@\"" > /usr/bin/wget && chmod +x /usr/bin/wget
	fi
exit 0

elif [ "$1" = "opkg_revert_to_default" ]; then

	if grep "#!/bin/sh" /usr/bin/wget; then
		rm /usr/bin/wget
		mv /usr/bin/wget.bak /usr/bin/wget
	fi
exit 0

elif [ "$1" = "daemon" ]; then
	uci revert mesh11sd
	uci revert wireless
	uci revert dhcp
	debugtype="notice"
	syslogmessage="mesh11sd is in startup"
	write_to_syslog

	if [ -f "/tmp/mesh_rssi_threshold" ]; then
		rm /tmp/mesh_rssi_threshold
	fi

	if [ -f "/tmp/dhcp6probe" ]; then
		mv "/tmp/dhcp6probe" "/tmp/dhcp6probe.prev"
	fi

	#Default flags
	startup=4 # bit 2
	statusmode=2 # bit 1
	enabled=1 #bit 0

	# Initial conditions
	statusmode=0
	changed=0
	firstloop=1 # Set if we have just started
	daemon_startup=1 # For future watchdog tests
	autoconfig=0
	macfixup=0 # Used if we need to fix the mesh interface mac address
	mute=0 # Mute some debug messages in function calls
	previous_path_change_count=1

	# Clear the dhcp authoritative flag
	uci delete dhcp.@dnsmasq[0].authoritative 2>/dev/null

	# Initial Mode Flag
	mode=$(($startup + $statusmode + $enabled ))
	lastmode=0

	check_config_params

	arp_setup

	# Start the main loop
	while true; do
		mode=$(($startup + $statusmode + $enabled))

		syslogmessage=""

		if [ $mode -eq 5 ]; then
			# startup=4, statusmode=0, enabled=1
			startup=0
			statusmode=0
			mode=1
			syslogmessage="mesh11sd v$version has started: mesh management mode $mode"

		elif [ $mode -eq 4 ]; then
			# startup=4, statusmode=0, enabled=0
			startup=0
			statusmode=2
			mode=0
			syslogmessage="mesh11sd v$version has started: mesh status mode $mode"

		elif [ $mode -eq 3 ]; then
			# startup=0, statusmode=2, enabled=1
			startup=0
			statusmode=0
			mode=1
			syslogmessage="mesh11sd v$version has started: mesh management mode $mode"

		elif [ $mode -eq 2 ]; then
			# startup=0, statusmode=2, enabled=0
			startup=0
			statusmode=2
			mode=0
			syslogmessage="mesh11sd v$version has started: mesh status mode $mode"

		elif [ $mode -eq 1 ]; then
			# startup=0, statusmode=0, enabled=1
			startup=0
			statusmode=0
			mode=1
			syslogmessage="mesh11sd v$version has started, mesh management mode $mode"

		elif [ $mode -eq 0 ]; then
			# startup=0, statusmode=0, enabled=0
			startup=0
			statusmode=2
			mode=0
			syslogmessage="mesh11sd v$version has started: mesh status mode $mode"
		fi

		if [ $mode -ne $lastmode ]; then
			debugtype="notice"
			write_to_syslog
		fi

		lastmode=$mode
		meshindex=0
		meshconfigs=""

		if [ "$enabled" = 1 ]; then
			#get list of mesh configs
			meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s " $1}')

			if [ -z "$meshconfigs" ]; then
				# no mesh configuration yet
				debugtype="info"
				syslogmessage="No mesh interfaces detected yet.... Attempting auto configure"
				write_to_syslog

				############# Autoconfig

				if [ "$auto_config" -eq 1 ]; then
					auto_config
				else
					debugtype="err"
					syslogmessage="auto_config is disabled. Please configure a mesh interface or enable auto_config...."
					write_to_syslog
					sleep $checkinterval

					auto_config=$(uci get mesh11sd.setup.auto_config 2> /dev/null)

					if [ -z "$auto_config" ] || [ "$auto_config" -gt 0 ]; then
						auto_config=1
					fi

					syslogmessage="option auto_config [ $auto_config ]"
					debugtype="info"
					write_to_syslog

				fi

				########################
			fi

			# Is the list of meshconfigs still empty?
			if [ -z "$meshconfigs" ]; then
				meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s " $1}')
			fi

			if [ ! -z "$meshconfigs" ]; then
				# we have an existing mesh config

				for meshconfig in $meshconfigs; do

					if [ $meshindex -gt 15 ]; then
						# Max number of mesh interfaces exceeded - abort
						debugtype="warn"
						syslogmessage="Maximum number of mesh interfaces exceeded"
						write_to_syslog
						break
					fi

					meshindex=$(($meshindex+1))
				done

				# set mesh mac address for this meshconfig
				refresh_bridgemac
				. /tmp/devicemac
				get_wiphys

				#### Disable macfixup:
				# macfixup=1

				if [ "$macfixup" -eq 0 ]; then
					for wiphy in $wiphys; do
						ucimeshconfig=$(uci get $meshconfig.macaddr 2> /dev/null)

						if [ -z "$ucimeshconfig" ]; then
							# No mac address fixup, so add one
							phyindex=$(echo "$wiphy" | awk -F "phy" '{printf "%s", $2}')
							phyindex=$(printf "%x" $phyindex)
							mesh_ifname="$mesh_basename""$phyindex"
							convert_to_la "$devicemac"
							debugtype="notice"
							syslogmessage="Setting mac address of mesh interface $mesh_ifname to [ $mac_la ]"
							write_to_syslog

							ucibatch="set wireless.m11s$phyindex.macaddr='$mac_la'"
							echo "$ucibatch" | uci batch
							changed=1
						fi
					done

					macfixup=1
				fi
			else
				continue
			fi

			if [ "$firstloop" -eq 1 ] || [ "$changed" -eq 1 ]; then
				firstloop=0
				changed=0

				touch /tmp/meshinterface
				all_nodes_rssi_update
			fi
			# get a list of interfaces
			get_mesh_iflist

			for iface in $iflist; do
				wait_for_interface "$iface"

				if [ "$ifstatus" = "down" ]; then
					continue
				fi

				debugtype="debug"
				syslogmessage="interface $iface is $ifstatus"
				write_to_syslog

				# get list of mesh parameters for this interface
				get_params

				if [ -z "$params" ]; then
					# this is not a mesh interface
					continue
				else
					# Check if this interface has a uci ifname
					uciname=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "wireless.%s" $2}')

					if [ -z "$uciname" ]; then
						# Error - No interface name in config, we should have added one
						debugtype="err"
						syslogmessage="Error getting mesh interface name"
						write_to_syslog
						continue
					fi
				fi

				# Set stp path cost
				mute=1
				get_mesh_path_cost

				if [ "$mesh_path_cost" -gt 0 ] && [ "$mesh_path_cost" -lt 65535 ]; then
					# Make sure STP is on
					stpstatus=$(brctl show $device | grep "yes" | awk '{printf "%s", $3}')

					if [ -z "$stpstatus" ] || [ "$stpstatus" != "yes" ]; then
						brctl stp "$device" yes
					fi

					brctl setpathcost "$device" "$iface" "$mesh_path_cost"

					br_ifaces=$(brctl showstp "$device" | grep -B 1 -w "port id" | grep "(" | awk '{printf "%s ", $1}')

					for br_iface in $br_ifaces; do

						if [ "$br_iface" = "$iface" ]; then
							continue
						fi

						other_path_cost=$(( 65535 - $mesh_path_cost))
						brctl setpathcost "$device" "$br_iface" "$other_path_cost"

					done
				elif [ "$mesh_path_cost" -eq 0 ]; then
					brctl stp "$device" no
				fi

				#Do NOT configure parameters found in wireless config
				# so change the value of uciname before calling check_mesh_params.
				#
				#Override wireless config and/or set parameters to those found in mesh11sd config
				uciname="mesh11sd.mesh_params"
				check_mesh_params
				check_mesh_phantom
			done

			if [ "$portal_detect" -eq 1 ]; then
				check_portal
			else
				# portal_detect is not enabled - force portal mode:
				authoritative=$(uci get dhcp.@dnsmasq[0].authoritative 2>/dev/null | awk '{printf "%d", $1}')

				if [ -z "$authoritative" ]; then
					authoritative=0
				fi

				uci set mesh11sd.mesh_params.mesh_connected_to_as='1'

				if [ -z "$authoritative" ] || [ "$authoritative" -eq 0 ]; then
					debugtype="notice"
					syslogmessage="Portal detection is disabled, forcing portal mode"
					write_to_syslog

					if [ -f "/var/run/udhcpc-$device.pid" ]; then
						kill $(cat /var/run/udhcpc-$device.pid)
					fi

					restore_ipv4

					uci set dhcp.@dnsmasq[0].authoritative='1'
					echo "set dhcp.$auto_mesh_network.ignore='0'" | uci batch
					echo "set dhcp.$auto_mesh_network.ra_default='2'" | uci batch
					echo "set dhcp.$auto_mesh_network.dhcpv6='server'" | uci batch
					echo "set dhcp.$auto_mesh_network.ra='server'" | uci batch
					uci set dhcp.@dnsmasq[0].logfacility='-'
					uci set dhcp.@dnsmasq[0].quietdhcp='1'

					mesh_hwmp_rootmode="4"
					echo "set mesh11sd.mesh_params.mesh_hwmp_rootmode='$mesh_hwmp_rootmode'" | uci batch

					/sbin/service dnsmasq restart
					/sbin/service odhcpd restart
					restart_mesh
				fi
			fi

			check_gate
			check_hwmp
			wait_for_mesh

		fi

		block_bridge_loops
		check_path_changes
		debugtype="debug"
		syslogmessage="checkinterval $checkinterval seconds"
		write_to_syslog

		sleep $checkinterval
	done
	exit 0

else
	echo "Unrecognised command - For help, try mesh11sd --help "
fi
